const COLORLIST = ['black', 'red'];
const CHESSMAN = [
  {
    name: ['将', '帅'],
    number: 1,
  },
  {
    name: ['士', '士'],
    number: 2,
  },
  {
    name: ['象', '相'],
    number: 2,
  },
  {
    name: ['马', '马'],
    number: 2,
  },
  {
    name: ['车', '车'],
    number: 2,
  },
  {
    name: ['炮', '炮'],
    number: 2,
  },
  {
    name: ['卒', '兵'],
    number: 5,
  },
];
const SETTING = {
  WIDTH: 4,
  HEIGHT: 4,
  MAX_MAN_IN_BOARD: 3,
  HORSE_RULE: false,
  ELEPHANT_RULE: false,
  CANNON_RULE: false,
  GENERAL_DIE: true,
}

class Game {
  constructor(ui) {
    this.ui = ui;
    this.table = Array.from(Array(SETTING.HEIGHT), () => new Array(SETTING.WIDTH));
    this.turn = COLORLIST[0];
    this.box = {};
    this.left = {};
    this.inBoard = {};
    for (let i in COLORLIST) {
      let color = COLORLIST[i];
      this.box[color] = [];
      this.left[color] = 0;
      this.inBoard[color] = 0;
      for (let m of CHESSMAN) {
        for (let _ = 0; _ < m.number; ++_) {
          this.box[color].push({ name: m.name[i], color });
          ++this.left[color];
        }
      }
    }
    console.log(this);
  }

  adversaries(color) {
    return COLORLIST[color == COLORLIST[0] ? 1 : 0];
  }


  turnPlayer() {
    this.turn = this.adversaries(this.turn);
  }

  getMan({ x, y }) {
    return this.table[x][y];
  }

  setMan({ x, y }, m) {
    if (m) this.table[x][y] = m;
    else delete this.table[x][y];
  }

  getCountInLine(posA, posB, d) {
    let p = { x: posA.x, y: posA.y };
    let tr = { x: Math.sign(d.x), y: Math.sign(d.y) };
    let ans = 0;
    while (p.x += tr.x, p.y += tr.y, p.x != posB.x || p.y != posB.y)
      if (this.getMan(p)) ++ans;
    return ans;
  }

  replace(pos, m) {
    let n = this.getMan(pos);
    if (n) --this.left[n.color], --this.inBoard[n.color];
    this.setMan(pos, m);
    if (m) ++this.left[m.color], ++this.inBoard[m.color];
    this.ui.setTable(pos);
  }

  move(posA, posB) {
    let m = this.getMan(posA);
    let n = this.getMan(posB);
    if (!this.canMove(posA, posB, m, n)) return false;
    this.replace(posA);
    this.replace(posB, m);
    if (this.left[this.adversaries(this.turn)] == 0 ||
      SETTING.GENERAL_DIE && n?.name == '将' || n?.name == '帅')
      this.ui.end(this.turn);
    this.turnPlayer();
    return true;
  }

  canMove(posA, posB, m, n) {
    if (m.color == n?.color) return false;
    let d = { x: posB.x - posA.x, y: posB.y - posA.y };
    let ad = { x: Math.abs(d.x), y: Math.abs(d.y) };
    if (ad.x + ad.y == 1 && !n) return true;
    switch (m.name) {
      case '将':
      case '帅':
      case '卒':
      case '兵':
        return ad.x + ad.y == 1;
      case '士':
        return ad.x == 1 && ad.y == 1;
      case '象':
      case '相': {
        if (ad.x != 2 || ad.y != 2) return false;
        let center = { x: (posA.x + posB.x) / 2, y: (posA.y + posB.y) / 2 };
        let l = this.getMan(center);
        if (l && !SETTING.ELEPHANT_RULE) return false;
        if (l?.color != m.color) this.replace(center);
        return true;
      }
      case '马': {
        let head = ad.x > ad.y ? 'x' : 'y';
        if (ad.x + ad.y != 3 || ad[head] != 2) return false;
        let headpos = { x: posA.x, y: posA.y };
        headpos[head] += d[head] / 2;
        let l = this.getMan(headpos);
        return !l || SETTING.HORSE_RULE && l.color == m.color;
      }
      case '车':
        return (d.x == 0 || d.y == 0) && this.getCountInLine(posA, posB, d) == 0;
      case '炮':
        return (d.x == 0 || d.y == 0) && this.getCountInLine(posA, posB, d) == (n ? 1 : 0) ||
          SETTING.CANNON_RULE && n && this.getCountInLine(posA, posB, d) == 1;
    }
  }

  add({ color, index }, pos) {
    if (this.getMan(pos) || this.inBoard[color] >= SETTING.MAX_MAN_IN_BOARD)
      return false;
    //delete this.box[turn][index];
    --this.left[color];
    this.replace(pos, this.box[color][index]);
    this.ui.removeFromBox({ color, index });
    this.turnPlayer();
    return true;
  }
}